package yjj.filter;

import cn.hutool.core.bean.BeanUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import yjj.constant.AuthContants;
import yjj.model.security.LoginUser;
import yjj.model.web.Result;
import yjj.utils.JwtUtils;
import yjj.utils.MyBeanUtil;
import yjj.utils.RedisUtil;
import yjj.utils.ServletUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;


@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    RedisUtil redisUtils;

    private JwtUtils jwtUtils;
    private final AuthenticationEntryPoint authenticationEntryPoint;
    // 定义无需认证的路径
    private static final List<String> EXCLUDE_URLS = Arrays.asList(
            "/login",
            "/swagger-resources/**",
            "/v3/**",
            "/webjars/**",
            "/doc.html",
            "/captcha/**",
            "/favicon.ico"
    );
    public JwtAuthenticationTokenFilter( RedisUtil redisUtils, JwtUtils jwtUtils, AuthenticationEntryPoint authenticationEntryPoint) {
        this.redisUtils = redisUtils;
        this.jwtUtils = jwtUtils;
        this.authenticationEntryPoint = authenticationEntryPoint;
    }
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String path = request.getRequestURI();
        log.info("path = " + path);
        // 如果请求路径无需认证，直接放行
        if (EXCLUDE_URLS.stream().anyMatch(pattern -> new AntPathMatcher().match(pattern, path))) {
            filterChain.doFilter(request, response);
            return;
        }

        String token = request.getHeader("Authorization");
        //对于不会携带token的请求直接放行 例如swagger的请求
        if (StringUtils.hasText(token)) {
            //判断token是否过期 看看能不能从redis里面取出来
            if (jwtUtils.isTokenExpired(token)) {
                log.warn("Access Token 已过期");
                // 返回提示客户端重新登录
                ServletUtils.renderResult(response, Result.error("Access Token 已过期，请重新登录"));
                return;
            }
            // 获取 Token 剩余时间
            long remainingTime = jwtUtils.getRemainingTime(token);
            log.info("Access Token 剩余时间: {} 毫秒", remainingTime);

            // 如果剩余时间低于阈值，生成新的 Token
            if (remainingTime < AuthContants.REFRESH_TOKEN_THRESHOLD) {
                String username = jwtUtils.getClaims(token).getSubject();
                String newAccessToken = jwtUtils.generateToken(username);
                // 将新的 Token 添加到响应头中，前端可以从响应头中获取并更新
                response.setHeader("Authorization", newAccessToken);
                log.info("生成新的 Access Token，并添加到响应头");
            }

            // 解析token获取用户id
            String username = jwtUtils.getClaims(token).getSubject();
            // 从redis中获取用户信息
            LoginUser loginUser = (LoginUser) redisUtils.get(AuthContants.REDIS_KEY_AUTH_USER_DETAIL + username);
            if (loginUser == null) {
                log.warn("Redis 中不存在用户信息");
                ServletUtils.renderResult(response, Result.error("Access Token 已过期，请重新登录"));
                return;
            }

            //需要将 Authentication 对象设置到 SecurityContext 中。如果没有正确设置，Spring Security 会认为请求未经过身份验证。
            // 将用户信息存入上下文 principal: 用户信息   credentials: 密码   authorities: 权限
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, loginUser.getPassword(), loginUser.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }

        filterChain.doFilter(request, response);

    }
}
